package org.crown.project.api.wechat;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.crown.common.jwt.JwtTokenUtil;
import org.crown.common.utils.HttpKit;
import org.crown.common.utils.bean.BeanUtils;
import org.crown.common.wechat.WXBizMsgCrypt;
import org.crown.common.wechat.XMLParse;
import org.crown.project.api.module.wechatuser.domain.TbWechatUser;
import org.crown.project.api.module.wechatuser.service.TbWechatUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Encoder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author WuFengSheng
 */
@Slf4j
@Service
public class WxService {

    @Autowired
    private WxProperties wxProperties;

    @Autowired
    private TbWechatUserService tbWechatUserService;

    @Autowired
    protected JwtTokenUtil jwtTokenUtil;

    /**
     * 创建token
     *
     * @return
     */
    private synchronized String createToken(String userName, Long userId) {
        String randomKey = jwtTokenUtil.getRandomKey();
        String token = jwtTokenUtil.generateToken(userName, randomKey, userId);
        return token;
    }

    /**
     * 微信小程序授权
     *
     * @param code
     * @return
     */
    private synchronized JSONObject weChatMiniAuth(String code) {
        log.info("code={}", code);
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxProperties.getAppId() + "&secret=" + wxProperties.getAppSecret() + "&js_code=" + code + "&grant_type=authorization_code";
        RestTemplate restTemplate = new RestTemplate();
        String respJson = restTemplate.getForObject(url, String.class);
        try {
            respJson = new String(respJson.getBytes("iso8859-1"), "utf-8");
            log.info("respJson={}", respJson);
            return JSONObject.parseObject(respJson);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 小程序登录
     *
     * @param code
     * @return
     */
    public TbWechatUser weChatMiniLogin(String code) {
        JSONObject jsonObj = this.weChatMiniAuth(code);
        log.info("jsonObj={}", jsonObj);
        String openid = jsonObj.getString("openid");
        String sessionKey = jsonObj.getString("session_key");
        TbWechatUser tbWechatUser = tbWechatUserService.getOne(new QueryWrapper<TbWechatUser>().eq("open_id", openid));
        if (tbWechatUser == null) {
            tbWechatUser = TbWechatUser.builder().openId(openid).sessionKey(sessionKey).build();
            if(tbWechatUserService.save(tbWechatUser)){
                String token = this.createToken(tbWechatUser.getOpenId(), tbWechatUser.getId());
                tbWechatUser.setToken(token);
                tbWechatUser.setLoginToken(token);
                tbWechatUserService.updateById(tbWechatUser);
                return tbWechatUser;
            }
        } else {
            tbWechatUser.setOpenId(openid);
            tbWechatUser.setSessionKey(sessionKey);
            if (tbWechatUserService.updateById(tbWechatUser)) {
                String token = this.createToken(tbWechatUser.getOpenId(), tbWechatUser.getId());
                tbWechatUser.setToken(token);
                tbWechatUser.setLoginToken(token);
                tbWechatUserService.updateById(tbWechatUser);
                return tbWechatUser;
            }
        }
        return null;
    }

    /**
     * 保存微信用户信息
     *
     * @param encryptedData
     * @param iv
     * @return
     */
    public TbWechatUser userInfo(Long userId, String encryptedData, String iv) {
        TbWechatUser tbWechatUser = tbWechatUserService.getById(userId);
        if (tbWechatUser != null) {
            String sessionKey = tbWechatUser.getSessionKey();
            String jsonStr = WxAesUtil.decrypt(encryptedData, sessionKey, iv, "utf-8");
            JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            log.info("jsonObj={}", jsonObj);
            String openId = jsonObj.getString("openId");
            String unionId = jsonObj.containsKey("unionId") ? jsonObj.getString("unionId") : null;
            Integer gender = jsonObj.containsKey("gender") ? jsonObj.getInteger("gender") : null;
            String avatarUrl = jsonObj.containsKey("avatarUrl") ? jsonObj.getString("avatarUrl") : null;
            String country = jsonObj.containsKey("country") ? jsonObj.getString("country") : null;
            String province = jsonObj.containsKey("province") ? jsonObj.getString("province") : null;
            String city = jsonObj.containsKey("city") ? jsonObj.getString("city") : null;
            String nickName = jsonObj.containsKey("nickName") ? jsonObj.getString("nickName") : null;

            if(nickName != null){
                BASE64Encoder encoder = new BASE64Encoder();
                tbWechatUser.setNickName(encoder.encode(nickName.getBytes()));
            }
            tbWechatUser.setOpenId(openId);
            tbWechatUser.setUnionId(unionId);
            tbWechatUser.setAvatarUrl(avatarUrl);
            tbWechatUser.setGender(gender);
            tbWechatUser.setCountry(country);
            tbWechatUser.setProvince(province);
            tbWechatUser.setCity(city);
            if (tbWechatUserService.updateById(tbWechatUser)) {
                return tbWechatUser;
            }
        }
        return null;
    }

    /**
     * 微信小程用户手机号
     *
     * @param userId
     * @param encryptedData
     * @param iv
     * @return
     */
    public TbWechatUser phoneNumber(Long userId, String encryptedData, String iv) {
        TbWechatUser tbWechatUser = tbWechatUserService.getById(userId);
        if (tbWechatUser != null) {
            String sessionKey = tbWechatUser.getSessionKey();
            String jsonStr = WxAesUtil.decrypt(encryptedData, sessionKey, iv, "utf-8");
            JSONObject jsonObj = JSONObject.parseObject(jsonStr);
            log.info("jsonObj={}", jsonObj);
            if (jsonObj.containsKey("phoneNumber")) {
                // purePhoneNumber countryCode
                tbWechatUser.setPhone(jsonObj.getString("phoneNumber"));
                if (tbWechatUserService.updateById(tbWechatUser)) {
                    return tbWechatUser;
                }
            }
        }
        return null;
    }

    /**
     * 获取当前的地理位置
     *
     * @param userId
     * @param entity
     * @return
     */
    public TbWechatUser getLocation(Long userId, TbWechatUser entity) {
        TbWechatUser tbWechatUser = tbWechatUserService.getById(userId);
        if (tbWechatUser != null) {
            BeanUtils.copyNoValueProperties(tbWechatUser, entity);
            if (tbWechatUserService.updateById(tbWechatUser)) {
                return tbWechatUser;
            }
        }
        return null;
    }

    /**
     * 打开地图选择位置
     *
     * @param userId
     * @param entity
     * @return
     */
    public TbWechatUser chooseLocation(Long userId, TbWechatUser entity) {
        TbWechatUser tbWechatUser = tbWechatUserService.getById(userId);
        if (tbWechatUser != null) {
            BeanUtils.copyNoValueProperties(tbWechatUser, entity);
            if (tbWechatUserService.updateById(tbWechatUser)) {
                return tbWechatUser;
            }
        }
        return null;
    }

    /**
     * 获取小程序全局唯一后台接口调用凭据，调调用绝大多数后台接口时都需使用 access_token，开发者需要进行妥善保存
     *
     * @return
     */
    public JSONObject getSystemToken() {
        RestTemplate restTemplate = new RestTemplate();
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wxProperties.getAppId() + "&secret=" + wxProperties.getAppSecret();
        String respJson = restTemplate.getForObject(url, String.class);
        try {
            respJson = new String(respJson.getBytes("iso8859-1"), "utf-8");
            log.info("respJson={}", respJson);
            return JSONObject.parseObject(respJson);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取小程序码，适用于需要的码数量极多的业务场景。通过该接口生成的小程序码，永久有效，数量暂无限制
     *
     * @param scene
     * @param page
     * @return
     */
    public void getWXACodeUnlimit(String scene, String page, boolean saveDisk) {
        JSONObject json = getSystemToken();
        if (!json.containsKey("access_token")) {
            return;
        }
        String accessToken = json.getString("access_token");

        RestTemplate restTemplate = new RestTemplate();
        String url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken;

        JSONObject form = new JSONObject();
        // 最大32个可见字符，只支持数字，大小写英文以及部分特殊字符：!#$&'()*+,/:;=?@-._~，其它字符请自行编码为合法字符（因不支持%，中文无法使用 urlencode 处理，请使用其他编码方式）
        form.put("scene", scene);
        // 必须是已经发布的小程序存在的页面（否则报错），例如 pages/index/index, 根路径前不要填加 /,不能携带参数（参数请放在scene字段里），如果不填写这个字段，默认跳主页面
        if (page != null) {
            form.put("page", page);
        }
        // 二维码的宽度，单位 px，最小 280px，最大 1280px
//        form.put("width",280);

        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        HttpEntity<String> formEntity = new HttpEntity<>(form.toJSONString(), headers);
        ResponseEntity<Resource> entity = restTemplate.postForEntity(url, formEntity, Resource.class);
        log.info("entity={}", entity);
        if (entity.getStatusCode() == HttpStatus.OK) {
            headers = entity.getHeaders();

            long contentLength = headers.getContentLength();

            ContentDisposition contentDisposition = headers.getContentDisposition();
            String filename = contentDisposition.getFilename();

            MediaType mediaType = headers.getContentType();
            String subtype = mediaType.getSubtype();

            filename = filename + "." + subtype;

            OutputStream out = null;
            try {
                InputStream in = entity.getBody().getInputStream();
                byte[] buffer = new byte[in.available()];
                in.read(buffer);

                if (saveDisk) {
                    File targetFile = new File(wxProperties.getQrcodeFilePath() + filename);
                    out = new FileOutputStream(targetFile);
                } else {
                    HttpServletResponse response = HttpKit.getResponse();
                    response.setContentType("image/jpeg");
                    response.addHeader("Content-disposition", "attachment;filename=" + contentDisposition.getFilename());
                    response.setContentLengthLong(contentLength);
                    out = response.getOutputStream();

                }
                out.write(buffer);
                out.flush();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return;
    }

    /**
     * 模板消息推送
     *
     * @param templateId      模板ID
     * @param receiveOpenid   接收消息者openid
     * @param formId          表单提交场景下，为 submit 事件带上的 formId；支付场景下，为本次支付的 prepay_id（必须是提交者的openid的formId对应，否则无法发送）
     * @param data            模板内容，不填则下发空模板
     * @param emphasisKeyword 模板需要放大的关键词，不填则默认无放大
     * @param page            点击模板卡片后的跳转页面，仅限本小程序内的页面。支持带参数,（示例index?foo=bar）
     * @return
     */
    public boolean pushMessage(String templateId, String receiveOpenid, String formId, JSONObject data, String emphasisKeyword, String page) throws Exception {
        log.info("templateId={},receiveOpenid={},data={}", templateId, receiveOpenid, data);
        // 获取系统token
        JSONObject json = getSystemToken();
        if (!json.containsKey("access_token")) {
            return false;
        }
        String accessToken = json.getString("access_token");

        if (accessToken == null) {
            return false;
        }
        String url = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=" + accessToken;
        RestTemplate restTemplate = new RestTemplate();
        try {
            HttpHeaders headers = new HttpHeaders();
            MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
            headers.setContentType(type);
            headers.add("Accept", MediaType.APPLICATION_JSON.toString());

            JSONObject templateJson = new JSONObject();
            templateJson.put("touser", receiveOpenid);
            templateJson.put("template_id", templateId);
            if (page != null) {
                templateJson.put("page", page);
            }
            templateJson.put("form_id", formId);
            templateJson.put("data", data);
            if (emphasisKeyword != null) {
                templateJson.put("emphasis_keyword", emphasisKeyword);
            }
            log.info("templateJson={}", templateJson.toString());

            HttpEntity<String> formEntity = new HttpEntity<String>(templateJson.toString(), headers);
            String respJson = restTemplate.postForObject(url, formEntity, String.class);
            log.info("response={}", respJson);
            JSONObject responseJson = JSONObject.parseObject(respJson);
            if (responseJson.containsKey("errcode")) {
                int errcode = (Integer) responseJson.get("errcode");
                String errmsg = (String) responseJson.get("errmsg");
                if (errcode == 0 && "ok".equals(errmsg)) {
                    return true;
                }
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 车辆预约通知
     *
     * @param receiveOpenid
     * @param formId
     * @param data
     * @param emphasisKeyword
     * @return
     */
    public boolean pushCarMessage(String receiveOpenid, String formId, JSONObject data, String emphasisKeyword) throws Exception {
        /**
         * 模板ID
         */
        String templateId = "JgCDGevaKDxlM03RpRXk2Mt6mMOczaSZykyYaLEmR6c";
        return pushMessage(templateId, receiveOpenid, formId, data, null, null);
    }

    /**
     String receiveOpenid = "oIdkY4yFVlS7S_25JIfRg1bCrP2o";
     String formId = "1534833905436";
     try {
     JSONObject noticeData = new JSONObject();
     JSONObject keyword1 = new JSONObject();
     keyword1.put("value", "2018-08-22");
     JSONObject keyword2 = new JSONObject();
     keyword2.put("value", "闽DF682H");
     JSONObject keyword3 = new JSONObject();
     keyword3.put("value", "吴逢生");
     JSONObject keyword4 = new JSONObject();
     keyword4.put("value", "18558732683");

     noticeData.put("keyword1", keyword1);
     noticeData.put("keyword2", keyword2);
     noticeData.put("keyword3", keyword3);
     noticeData.put("keyword4", keyword4);
     weixinService.pushCarMessage(receiveOpenid,formId,noticeData,null);
     }catch (Exception e){
     e.printStackTrace();
     }
     }
     */


    /**
     * 获取request
     *
     * @param request
     * @return
     */
    private String getRequestPostStr(HttpServletRequest request){
        try{
            int contentLength = request.getContentLength();
            if(contentLength<0){
                return null;
            }
            byte[] buffer = new byte[contentLength];
            for (int i = 0; i < contentLength;) {
                int readlen = request.getInputStream().read(buffer, i,contentLength - i);
                if (readlen == -1) {
                    break;
                }
                i += readlen;
            }
            String charEncoding = request.getCharacterEncoding();
            if (charEncoding == null) {
                charEncoding = "UTF-8";
            }
            return new String(buffer, charEncoding);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 接收微信客服信息
     *
     * @param request
     * @param msgSignature
     * @param timestamp
     * @param nonce
     * @return
     */
    public String acceptMessage(HttpServletRequest request, String msgSignature, String timestamp, String nonce){
        String fromXml = getRequestPostStr(request);
        /**
         * <xml>
         * <ToUserName><![CDATA[gh_b6d06396759c]]></ToUserName>
         * <FromUserName><![CDATA[oD8bq4qqQaqrOg6rlYV8KwXOzuGU]]></FromUserName>
         * <CreateTime>1583650967</CreateTime>
         * <MsgType><![CDATA[text]]></MsgType>
         * <Content><![CDATA[你好]]></Content>
         * <MsgId>22672429352879344</MsgId>
         * <Encrypt><![CDATA[g1heDTCSs+z5E/aFDy5BQOiiL1e15ceoi98Jk0OH+C6Udglk2XghC267VCJUuxuWRY/m+x9LmuLb/EzyXsBKrsmpZsgNlZfhEuxfNLj8ykOUEgbaLAxJQHF3gdNwaxvKptL+4RuotEn47yA1+5IoUbosxq7zNZxdlbsyRP/EUZnftwV85Vk34BtMeBtu4aEC11p2UNZem7Kn3QIyPaTjdCobUAnSLKi25NJzU1s0wQ4zpC5qCAMDsheiz6XS6xsJkcjqs7thH2KFLoOfb3hzfMFgnmMs3XhJ68OXs+H5ib+0YobRas0zQjxjHCFI2S4le3Fi/BMOZRysfakEkGU8sHy1b7PEkWs2yUP4ROJlCSC6O0W2j8XCjIyAs64IhLqe19IJ8oRhweINv9x75/8060v6SXcJdzJkBJyoD2vBOsI=]]></Encrypt>
         * </xml>
         */
        log.info("fromXml={}", fromXml);
        try {

            JSONObject jsonObj = XMLParse.xmlToJson(fromXml);
            log.info("jsonObj={}", jsonObj);
            String openId = jsonObj.getString("FromUserName");

            WXBizMsgCrypt pc = new WXBizMsgCrypt(wxProperties.getToken(), wxProperties.getEncodingAesKey(), wxProperties.getAppId());
            String result = pc.decryptMsg(msgSignature, timestamp, nonce, fromXml);
            System.out.println("解密后明文: " + result);

            return "success";
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

}
